﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using System.Net;
using System.Security.Cryptography;
using System.Text;

namespace test1
{
    public class FetchHelp
    {
        private readonly byte[] key = Encoding.UTF8.GetBytes("8?)i_~Nk7qv0IX;2");
        private Aes aes = Aes.Create();
        private DbHelp db = new DbHelp();
        private Random rand = new Random(DateTime.Now.Microsecond);

        private readonly List<string> agents = new List<string>
        {
            "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36",
            "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14",
            "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)",
            "Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
            "Opera/9.25 (Windows NT 5.1; U; en)",
            "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
            "Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) (Kubuntu)",
            "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070731 Ubuntu/dapper-security Firefox/1.5.0.12",
            "Lynx/2.8.5rel.1 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/1.2.9",
            "Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.7 (KHTML, like Gecko) Ubuntu/11.04 Chromium/16.0.912.77 Chrome/16.0.912.77 Safari/535.7",
            "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0 ",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0"
        };

        private HttpClient getHttpClient(String proxyIp, int proxyPort)
        {
            if(!String.IsNullOrEmpty(proxyIp) && proxyPort > 0)
            {
                HttpClientHandler handler = new HttpClientHandler();
                handler.Proxy = new WebProxy(proxyIp, proxyPort);
                return new HttpClient(handler);
            }
            return new HttpClient();
        }

        public string post(string url, dynamic data, Dictionary<string, string> headers, string proxyIp, int proxyPort)
        {
            var heads = new Dictionary<string, string>(){
                { "authority","api.branddb.wipo.int"},
                {"method","POST"},
                { "path","/search" },
                { "scheme","https" },
                { "Accept","application/json, text/plain, */*" },
                { "Accept-Encoding","gzip, deflate, br, zstd" },
                {"Priority","u=1, i" },
                {"Origin","https://branddb.wipo.int" },
                {"Referer","https://branddb.wipo.int/" },
                {"User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0" }
            };
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    heads[header.Key] = header.Value;
                }
            }
            if (heads.ContainsKey("Content-Type"))
            {
                heads.Remove("Content-Type");
            }
            heads["User-Agent"] = agents[rand.Next(agents.Count)];

            var client = getHttpClient(proxyIp, proxyPort);
            
            //{ "Content-Type","application/json"},
            HttpRequestMessage reqmsg = new HttpRequestMessage(HttpMethod.Post, url);
            
            foreach (var head in heads)
            {
                reqmsg.Headers.Add(head.Key, head.Value);
            }
            if (data is string)
            {
                reqmsg.Content = new StringContent(data);
            }
            else
            {
                reqmsg.Content = System.Net.Http.Json.JsonContent.Create(data);
            }
            reqmsg.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");

            var res = client.Send(reqmsg);

            if (res.StatusCode == HttpStatusCode.OK)
            {
                var str = new StreamReader(res.Content.ReadAsStream()).ReadToEnd();
                return decode(str);
            }
            throw new Exception(res.StatusCode.ToString());
        }

        public string get(string url, Dictionary<string, string> headers, string proxyIp, int proxyPort)
        {
            var heads = new Dictionary<string, string>(){
                { "authority","api.branddb.wipo.int"},
                {"method","GET"},
                { "scheme","https" },
                { "Accept","application/json, text/plain, */*" },
                { "Accept-Encoding","gzip, deflate, br, zstd" },
                {"Priority","u=1, i" },
                {"Origin","https://branddb.wipo.int" },
                {"Referer","https://branddb.wipo.int/" },
                {"User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0" }
            };
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    heads[header.Key] = header.Value;
                }
            }
            var client = getHttpClient(proxyIp, proxyPort);
            if (heads.ContainsKey("Content-Type"))
            {
                heads.Remove("Content-Type");
            }
            heads["User-Agent"] = agents[rand.Next(agents.Count)];
            HttpRequestMessage reqmsg = new HttpRequestMessage(HttpMethod.Get, url);
            foreach (var head in heads)
            {
                reqmsg.Headers.Add(head.Key, head.Value);
            }
            var res = client.Send(reqmsg);
            if (res.StatusCode == HttpStatusCode.OK)
            {
                var str = new StreamReader(res.Content.ReadAsStream()).ReadToEnd();
                return decode(str);
            }
            throw new Exception(res.StatusCode.ToString());
        }

        public string decode(string? cipherText)
        {
            if (string.IsNullOrEmpty(cipherText) || cipherText.Length < 10)
            {
                return cipherText;
            }
            try
            {
                byte[] buf = Convert.FromBase64String(cipherText);
                aes.Key = key;
                buf = aes.DecryptEcb(buf, PaddingMode.Zeros);
                var str = Encoding.UTF8.GetString(buf);
                var ix = str.LastIndexOf("}");
                return str.Substring(0, ix + 1);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                return cipherText;
            }
        }

        public SearchPage search(dynamic data, Dictionary<string, string> headers, String proxyIp, int proxyPort)
        {
            string url = "https://api.branddb.wipo.int/search";
            var str1 = post(url, data, headers, proxyIp, proxyPort);
            var obj = JsonConvert.DeserializeObject(str1) as JObject;
            obj = obj.GetValue("response") as JObject;
            var page = new SearchPage();
            page.numFound = (int)obj?.GetValue("numFound");
            page.start = (int)obj?.GetValue("start");
            var docs = obj?.GetValue("docs") as JArray;
            if (docs == null) return null;

            var list = new List<ST13Record>();
            foreach (var doc in docs)
            {
                var jdoc = (JObject)doc;
                var st13 = (String?)jdoc.GetValue("st13");
                if (!string.IsNullOrEmpty(st13))
                {
                    //var p = getDoc(st13);
                    var p = read4JsonObj(st13, jdoc, jdoc);
                    list.Add(p);
                }
            }
            page.docs = list;
            return page;
        }

        public ST13Record getDoc(string st13, Dictionary<string, string> headers, string proxyIp, int proxyPort)
        {
            var record = db.selectObjct<ST13Record>("select * from brands711_2 where st13=@st13", st13);
            if (record != null) return record;

            JObject? obj1;
            JObject? obj2;
            string logo = null;
            var userProxy = !String.IsNullOrEmpty(proxyIp) && proxyPort > 0;
            var row = db.selectRow("select txt1,txt2 from st13 where st13=@st13", st13);
            if (row == null)
            {
                var url1 = "https://api.branddb.wipo.int/docsidx?lang=en&st13=" + st13;
                string s1 = this.get(url1, headers, proxyIp, proxyPort);
                if (!userProxy)
                {
                    Thread.Sleep(rand.Next(100));
                }
                var url2 = "https://api.branddb.wipo.int/docsgbd?images=true@&lang=en&st13=" + st13;
                var s2 = this.get(url2, headers, proxyIp, proxyPort);
                if (!userProxy)
                {
                    Thread.Sleep(1000+rand.Next(100));
                }

                obj1 = JsonConvert.DeserializeObject(s1) as JObject;
                obj1 = obj1?.SelectToken("response.docs[0]") as JObject;

                obj2 = JsonConvert.DeserializeObject(s2) as JObject;
                obj2 = obj2?.SelectToken("response.docs[0]") as JObject;

                logo = savelogo(obj2, st13);

                s1 = JsonConvert.SerializeObject(obj1);
                s2 = JsonConvert.SerializeObject(obj2);

                //不用保存原始数据了，只保存解析后的数据就可以，节省空间
                //db.execute("insert into st13(st13,txt1,txt2) values(@st13, @txt1, @txt2)", st13, s1, s2);
            }
            else
            {
                string s1 = row[0];
                string s2 = row[1];
                obj1 = JsonConvert.DeserializeObject(s1) as JObject;
                obj2 = JsonConvert.DeserializeObject(s2) as JObject;
                logo = savelogo(obj2, st13);
            }
            record = read4JsonObj(st13, obj1, obj2);
            if (!string.IsNullOrEmpty(logo))
            {
                record.logo = logo;
            }
            save2db(record);
            return record;
        }

        void save2db(ST13Record p)
        {
            if (p == null) return;
            db.execute(@"insert into brands711_2(st13,logo,brandName,status,markFeature,office,type,applicationNumber,applicationDate,
                registrationNumber,registrationDate,expiryDate,kind,niceClass,applicantCountryCode,classificationCode,
                applicantsName,applicantsAddr,applicantsCountry,publicationDate,filingPlace,applicationLanguageCode,markVerbalElement,
                representativesKind,representativesName,representativesAddr,representativesCountry) values (
                @st13,@logo,@brandName,@status,@markFeature,@office,@type,@applicationNumber,@applicationDate,
                @registrationNumber,@registrationDate,@expiryDate,@kind,@niceClass,@applicantCountryCode,@classificationCode,
                @applicantsName,@applicantsAddr,@applicantsCountry,@publicationDate,@filingPlace,@applicationLanguageCode,@markVerbalElement,
                @representativesKind,@representativesName,@representativesAddr,@representativesCountry)", p);
        }

        String readVal(JObject? obj, String key)
        {
            if (obj == null) return "";
            bool isToken = key.IndexOf("[") != -1 || key.IndexOf(".") != -1;
            JToken? val;
            if (isToken)
            {
                val = obj?.SelectToken(key);
            }
            else
            {
                val = obj?.GetValue(key);
            }
            if (val == null) return "";
            var arr = val as JArray;
            if (arr == null)
            {
                if (key.EndsWith("Date"))
                {
                    var s1 = val.ToString();
                    try
                    {
                        var d1 = DateTime.Parse(s1);
                        return d1.ToString("yyyy/MM/dd");
                    }
                    catch (Exception)
                    {
                        if (s1.Length > 10)
                        {
                            return s1.Substring(0, 10);
                        }
                        return s1;
                    }
                }
                return val.ToString();
            }
            return String.Join(",", arr.Select(x => x.ToString()));
        }

        public ST13Record read4JsonObj(string st13, JObject? obj1, JObject? obj2)
        {
            try
            {
                var record = new ST13Record();
                record.st13 = st13;
                record.brandName = readVal(obj1, "brandName[0]");
                record.status = readVal(obj1, "status");
                record.markFeature = readVal(obj1, "markFeature");
                record.office = readVal(obj1, "office");
                record.type = readVal(obj1, "type");
                record.applicationNumber = readVal(obj1, "applicationNumber");
                record.applicationDate = readVal(obj1, "applicationDate");
                record.registrationNumber = readVal(obj1, "registrationNumber");
                record.registrationDate = readVal(obj1, "registrationDate");
                record.expiryDate = readVal(obj1, "expiryDate");
                record.kind = readVal(obj1, "kind");
                record.niceClass = readVal(obj1, "niceClass");
                record.applicantCountryCode = readVal(obj1, "applicantCountryCode");
                record.classificationCode = readVal(obj2, "markImageDetails[0].classification.code");
                record.applicantsName = readVal(obj2, "applicants[0].fullName[0].text");
                record.applicantsAddr = readVal(obj2, "applicants[0].fullAddress[0].text");
                record.applicantsCountry = readVal(obj2, "applicants[0].countryCode");
                record.publicationDate = readVal(obj2, "publicationDate");
                record.filingPlace = readVal(obj2, "filingPlace");
                record.applicationLanguageCode = readVal(obj2, "applicationLanguageCode");
                record.markVerbalElement = readVal(obj2, "wordMarkSpecification.markVerbalElement[0].text");

                record.representativesKind = readVal(obj2, "representatives[0].kind");
                record.representativesName = readVal(obj2, "representatives[0].fullName[0].text");
                record.representativesAddr = readVal(obj2, "representatives[0].fullAddress[0].text");
                record.representativesCountry = readVal(obj2, "representatives[0].countryCode");

                return record;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return null;
            }
        }



        public static readonly string LOGO_PATH = "logo";
        private string savelogo(JObject? obj, String st13)
        {
            var logo = (string?)obj?.SelectToken("logo[0]");
            if (!string.IsNullOrEmpty(logo))
            {
                obj.Remove("logo");
                byte[] buf = Convert.FromBase64String(logo);

                string path = Path.Combine(AppContext.BaseDirectory, LOGO_PATH);
                string fn = st13 + ".png";
                if (!Path.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                path = path + Path.DirectorySeparatorChar + fn;
                if (!System.IO.File.Exists(path))
                {
                    System.IO.File.WriteAllBytes(path, buf);
                }
                return "/" + LOGO_PATH + "/" + fn;
            }
            return null;
        }
    }
}
